﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "NdbObjectCloneable.h"
#include "NdbObjectSchematic.h"
#include "NdbDataInterface.h"
#include <unordered_map>

namespace gcmp
{
    //////////////////////////////////////////////////////////////////////////
    // ElementIdDetectorHelper。ReplaceElementIdHelper的加速器
    template <class T, class Enable = void>
    class ElementIdDetectorHelper
    {
    public:
        static bool DetectElementId(const T * /*value*/) 
        {
            // 1)非以下特化过的普通类型 2)裸指针
            // 不进行ElementId字段替换
            return false; 
        }
    };

    template <>
    class ElementIdDetectorHelper<ElementId>
    {
    public:
        static bool DetectElementId(const ElementId * /*value*/) { return true; }
    };

    template<class T>
    class ElementIdDetectorHelper<gcmp::OwnerPtr<T>>
    {
    public:
        static bool DetectElementId(const gcmp::OwnerPtr<T>* /*value*/)
        {
            // 对于智能指针，如果位于容器中，指向的对象各自是否含有ModelId类型的成员变量无法通过容器中第一个元素确定。
            // 所以这里假设是含有ElementId类型的成员变量的，在替换时发现没有的话会跳过。
            return true;
        }
    };

    template<class T>
    class ElementIdDetectorHelper<T, typename std::enable_if<std::is_base_of<NdbObjectCloneable, T>::value>::type>
    {
    public:
        static bool DetectElementId(const T* value)
        {
            return value->HasElementIdProperty();
        }
    };

    template<class T>
    class ElementIdDetectorHelper<T, typename std::enable_if<std::is_base_of<NdbObjectSchematic, T>::value>::type>
    {
    public:
        static bool DetectElementId(const T* value)
        {
            return gcmp::NdbDataInterface::DetectElementId(value);
        }
    };

    //////////////////////////////////////////////////////////////////////////
    // ReplaceElementIdHelper
    template<class T, class Enable = void>
    class ReplaceElementIdHelper
    {
    public:
        static void ReplaceElementId(T* value, const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap)
        {

        }
    };

    template<>
    class ReplaceElementIdHelper<ElementId>
    {
    public:
        static void ReplaceElementId(ElementId* value, const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap)
        {
            auto iter = oldToNewMap.find(*value);
            if (iter != oldToNewMap.end())
                *value = iter->second;
        }
    };

    template<class T>
    class ReplaceElementIdHelper<gcmp::OwnerPtr<T>>
    {
    public:
        static void ReplaceElementId(gcmp::OwnerPtr<T>* value, const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap)
        {
            if (value == nullptr)
                return;
            ReplaceElementIdHelper<T>::ReplaceElementId(value->get(), oldToNewMap);
        }

    };
    template<class T>
    class ReplaceElementIdHelper<T, typename std::enable_if<std::is_base_of<NdbObjectCloneable, T>::value>::type>
    {
    public:
        static void ReplaceElementId(NdbObjectCloneable* value, const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap)
        {
            value->ReplaceElementIds(oldToNewMap);
        }
    };

    template<class T>
    class ReplaceElementIdHelper<T, typename std::enable_if<std::is_base_of<NdbObjectSchematic, T>::value>::type>
    {
    public:
        static void ReplaceElementId(T* value, const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap)
        {
            gcmp::NdbDataInterface::ReplaceElementIds(value, oldToNewMap);
        }
    };
    template<class T>
    class ReplaceElementIdHelper<T, typename std::enable_if<std::is_base_of<IDbObject, T>::value && !std::is_base_of<NdbObjectSchematic, T>::value && !std::is_base_of<NdbObjectCloneable, T>::value>::type>
    {
    public:
        static void ReplaceElementId(T* value, const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap)
        {
            if (NdbObjectSchematic * pNdbObjectSchematic = thread_safe_quick_cast<NdbObjectSchematic>(value))
            {
                ReplaceElementIdHelper<NdbObjectSchematic>::ReplaceElementId(pNdbObjectSchematic, oldToNewMap);

            }
            else if (NdbObjectCloneable * pNdbObjectClonealbe = thread_safe_quick_cast<NdbObjectCloneable>(value))
            {
                ReplaceElementIdHelper<NdbObjectCloneable>::ReplaceElementId(pNdbObjectClonealbe, oldToNewMap);
            }
            else
            {
                // Do nothing now
            }
        }
    };
}